/**
 * Gui.java
 *
 * @author  Lobsang Alvites Szaler <l.alvites@lpcsite.com>
 * @author  Rodolfo Burlando Makthon <r.makthon@lpcsite.com>
 *
 * @version 1.0
 */

package sim.framework.gui;

import javax.microedition.lcdui.*;
import sim.framework.*;
import sim.framework.gui.*;
import sim.framework.font.*;
import java.util.*;


public abstract class Gui extends Canvas implements EventListener {
    
    /**
     * Identificadores de teclas
     *
     */
    public static final int KEY_NONE = 0;
    public static final int KEY_UP = 1;
    public static final int KEY_DOWN = 2;
    public static final int KEY_LEFT = 3;
    public static final int KEY_RIGHT = 4;
    public static final int KEY_OK = 5;
    public static final int KEY_SOFTLEFT = 6;
    public static final int KEY_SOFTRIGHT = 7;
    public static final int KEY_CLEAR = 8;
    public static final int KEY_POUND = 35; // #
    public static final int KEY_STAR = 42;  // *
    public static final int KEY_0 = 48;
    public static final int KEY_1 = 49;
    public static final int KEY_2 = 50;
    public static final int KEY_3 = 51;
    public static final int KEY_4 = 52;
    public static final int KEY_5 = 53;
    public static final int KEY_6 = 54;
    public static final int KEY_7 = 55;
    public static final int KEY_8 = 56;
    public static final int KEY_9 = 57;
    
    /**
     * Areas sincronizadas (Soporte Multi-thread)
     *
     */
    private static final int LOCKED_FOR_PAINT = 0;
    private static final int LOCKED_FOR_MESSAGE = 1;
    private static final int LOCKED_FOR_EVENT = 2;
    private static final int LOCKED_FOR_RENDER = 3;
    
    /**
     * Mensajes
     *
     */
    private static final int MSG = 0;
    private static final int MSG_ADDCTRL = 1;
    private static final int MSG_DELCTRL = 2;
    private static final int MSG_DELALLCTRLS = 3;
    private static final int MSG_SETFOCUS = 4;
    private static final int MSG_NEXTFOCUS = 5;
    private static final int MSG_PREVFOCUS = 6;
    private static final int MSG_ACTIVEALL = 7;
    private static final int MSG_REDRAW = 8;
    private static final int MSG_SHOW = 9;
    private static final int MSG_POPUPSHOW = 10;
    private static final int MSG_POPUPHIDE = 11;
    protected static final int MSG_USER = 100;
    
    /**
     * PopUp (Aspecto).
     *
     */
    private static final int POPUP_BORDER = 26;
    private static final int POPUP_SHADOW_W = 4;
    
    /**
     * Modificadores de desempeño
     *
     */
    private static final int CTRLS_INIT_SIZE = 10;
    
    /**
     * Propiedades
     *
     */
    protected Simlet        m_Simlet;
    protected Msg           m_Msg = new Msg();
    protected Vector        m_vecCtrl = null;
    
    private static int  m_SCREENWIDTH;
    private static int  m_SCREENHEIGHT;
    
    /**
     * Popup (Precalculo)
     */
    private String      m_strPopUp = null;
    private FontSingle  m_fntPopUp;
    private Vector      m_vecPopUpLines = new Vector(5);
    private int         m_popupStrX;
    private int         m_popupStrY;
    private int         m_popupBx;
    private int         m_popupBy;
    private int         m_popupBw;
    private int         m_popupBh;
    
    /***********************************************************/
    
    /**
     * GUI Constructor
     */
    public Gui( final Simlet s ) {
        m_Simlet = s;
        m_vecCtrl = new Vector(CTRLS_INIT_SIZE);
        
        setFullScreenMode(true);
        m_SCREENWIDTH = getWidth();
        m_SCREENHEIGHT = getHeight();
        
        m_Simlet.getFontSys().init(this);
        
        //setKeyDelay( KEYDELAY_MS / Renderer.TICK_MS );
        //setKeyState(false);
        
        setActive(true);
    }
    
    
    private synchronized int lock(int lockFor, int id, int msg,
				  int param1, Object param2) {
        
        int ret = 0;
        
        switch( lockFor ) {
	case Gui.LOCKED_FOR_PAINT:
	    lockedForPaint((Graphics)param2);
	    break;
	case Gui.LOCKED_FOR_MESSAGE:
	    ret = lockedForMessage(id, msg, param1, param2);
	    break;
	case Gui.LOCKED_FOR_EVENT:
	    ret = lockedForEvent((Msg)param2);
	    break;
	case Gui.LOCKED_FOR_RENDER:
	    ret = lockedForRender();
        }
        
        return ret;
    }
    
    private void lockedForPaint(final Graphics g) {
        int i, n;
        
        //#ifdef DEBUG_GUI
        //# System.out.println("Gui::lockGedForPaint() ENTERED");
        //#endif
        
        // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        //
        // El siguiente código debe desaparecer :
        //
        // - la función paint() de cada control lo puede resolver.
        //
        for( i = 0, n = m_vecCtrl.size(); i < n; i++) {
            if( ((GuiCtrl)m_vecCtrl.elementAt(i)).getActive() == true ) {
                ((GuiCtrl)m_vecCtrl.elementAt(i)).run();
            }
        }
        // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        
        // Redrawing GUI Controls
        GuiCtrl ctrl;
        Enumeration e;
        e = m_vecCtrl.elements();
        while( e.hasMoreElements() ) {
            ctrl = (GuiCtrl)e.nextElement();
            if( ctrl.m_bRepaint == true ) {
                // Repaints background behind a GUI Control
                g.setClip( 0, ctrl.getY(), getScreenWidth(), ctrl.getHeight() );
                paint_background(g);
                // Repaints the Control
                ctrl.paint(g);
                ctrl.m_bRepaint = false;
                //#ifdef DEBUG_GUI
                //# System.out.println("Gui::lockedForPaint() CTRL id# "+ctrl.getID()+" PAINTED");
                //#endif
            }
        }
        
        if( m_strPopUp != null ) {
            popUp_paint(g);
        }
        
        //#ifdef DEBUG_GUI
        //# System.out.println("Gui::lockedForPaint() EXITING");
        //#endif
    }
    
    /**
     *  Fills the background with a white.
     *  This method can be redefined to paint another picture.
     */
    protected void paint_background( final Graphics g ) {
        g.setColor(255, 255, 255);
        g.fillRect(0,0, getScreenWidth(), getScreenHeight());
    }
    
    /**
     *  Redraws all Controls.
     */
    private final void redraw_all() {
        Enumeration e = m_vecCtrl.elements();
        while( e.hasMoreElements() ) {
            GuiCtrl ctrl = (GuiCtrl)e.nextElement();
            ctrl.m_bRepaint = true;
        }
    }
    
    private final int lockedForMessage(int ctrlID, int msgID, int param1, Object param2) {
        int ret = 0;
        GuiCtrl ctrl;
        //#ifdef DEBUG_GUI
        //# System.out.println("Gui::lockedForMessage() ENTERED");
        //# System.out.println("msgID = " + msgID);
        //#endif
        
        switch (msgID) {
	case Gui.MSG_ADDCTRL:
	    ret = lockedForMessageAddCtrl((GuiCtrl)param2);
	    break;
	case Gui.MSG_DELCTRL:
	    ret = lockedForMessageDelCtrl(ctrlID, (GuiCtrl)param2);
	    break;
	case Gui.MSG_DELALLCTRLS:
	    ret = lockedForMessageDelAllCtrls();
	    break;
	case Gui.MSG_SETFOCUS:
	    ret = lockedForMessageSetFocus(ctrlID, (GuiCtrl)param2);
	    break;
	case Gui.MSG_NEXTFOCUS:
	    ret = lockedForMessageNextFocus();
	    break;
	case Gui.MSG_PREVFOCUS:
	    ret = lockedForMessagePrevFocus();
	    break;
	case MSG_ACTIVEALL:
	    ret = lockedForMessageActiveAll(param1);
	    break;
	case MSG_REDRAW:
	    ret = lockedForMessageRedraw();
	    break;
	case MSG_SHOW:
	    ret = lockedForMessageShow();
	    break;
	case MSG_POPUPSHOW:
	    lockedForMessagePopUpShow( (String)param2 );
	    break;
	case MSG_POPUPHIDE:
	    lockedForMessagePopUpHide();
	    break;
	default:
	    ctrl = getCtrl(ctrlID);
	    if(ctrl != null) {
		ret = ctrl.sendMessage(msgID, param1, param2 );
		ctrl.repaint();
	    }
        }
        
        //#ifdef DEBUG_GUI
        //# System.out.println("Gui::lockedForMessage() EXITING");
        //#endif
        
        return ret;
    }
    
    /**
     * Añade un control a la colección.
     *
     * @param ctrl
     *
     * @return Devuelve 1 si en cotrol fue añadido, en caso contrario
     *         devuelve 0. Un control puede dejar de ser añadido si ya
     *         existe en la lista. Nota: Dos controles pueden tener el
     *         mismo ID (p.e dos etiquetas estáticas), por lo tanto la
     *         condición de unicidad la establece el hash code de cada
     *         instancia.
     */
    private int lockedForMessageAddCtrl(GuiCtrl ctrl) {
        if( ctrl != null ) {
	    //            ctrl.setGui( this );
            m_vecCtrl.addElement(ctrl);
        }
        return 0;
    }
    
    private int lockedForMessageDelCtrl(int ctrlID, GuiCtrl ctrl) {
        int ret = 0;
        int c;
        if (ctrl != null) {
            // Borrar por instancia.
            ret = m_vecCtrl.removeElement(ctrl) ? 1 : 0;
        } else {
            // Borrar por ID. (Esto debe usar Enumeration)
            c = m_vecCtrl.size();
            for(int i=0; i<c;i++){
                if ( ( (GuiCtrl) m_vecCtrl.elementAt(i)).getID() == ctrlID) {
                    m_vecCtrl.removeElementAt(i); // Muy mala practica.
                    ret = 1;
                }
            }
        }
        return ret;
    }
    
    private int lockedForMessageDelAllCtrls() {
        return 0;
    }
    
    /******************************************************
     *  FOCUS
     ******************************************************/
    
    private final int lockedForMessageSetFocus( int ctrlID, GuiCtrl ctrl ) {
        if( m_vecCtrl != null ) {
            if( ctrl != null ) {
                if( setFocus(ctrl) == true ) {
                    return 1;
                }
            } else {
                Enumeration e = m_vecCtrl.elements();
                while( e.hasMoreElements() ) {
                    ctrl = (GuiCtrl)e.nextElement();
                    if( ctrl.getID() == ctrlID ) {
                        if( setFocus(ctrl) == true ) {
                            return 1;
                        }
                    }
                }
            }
        }
        return 0;
    }
    
    private final boolean setFocus( final GuiCtrl ctrl ) {
        if( ctrl != null ) {
            if( ctrl.focusable() == true ) {
                // Sets to False previous Control focus
                GuiCtrl ctrlPrev = getFocus();
                if( ctrlPrev != null ) {
                    ctrlPrev.setFocus(false);
                }
                ctrl.setFocus(true);
                return true;
            }
        }
        return false;
    }
    
    private final int lockedForMessageNextFocus() {
        Enumeration e;
        GuiCtrl ctrl = null;
        int     bFound = 0;
        
        if( m_vecCtrl != null ) {
            // Searches the next focusable Control
            // Steps enumeration for the actual Control owning the focus
            e = m_vecCtrl.elements();
            while( e.hasMoreElements() ) {
                ctrl = (GuiCtrl)e.nextElement();
                if( ctrl.focus() == true ) {
                    break;
                }
            }
            // Now steps forward looking for a focusable Control
            while( e.hasMoreElements() && bFound == 0 ) {
                ctrl = (GuiCtrl)e.nextElement();
                bFound = lockedForMessageSetFocus( -1, ctrl );
            }
            // No focusable Controls found. Sets the first one available
            if( bFound == 0 ) {
                if( initFocus() ) {
                    return 1;
                }
            }
        }
        return 0;
    }
    
    private final boolean initFocus() {
        Enumeration e;
        GuiCtrl     ctrl;
        if( m_vecCtrl != null ) {
            e = m_vecCtrl.elements();
            while( e.hasMoreElements() ) {
                ctrl = (GuiCtrl)e.nextElement();
                if( setFocus(ctrl) == true ) {
                    return true;
                }
            }
        }
        return false;
    }
    
    private final int  lockedForMessagePrevFocus() {
        Enumeration e;
        GuiCtrl ctrl = null;
        GuiCtrl ctrlPrev = null;
        int bFlag = 0;
        
        if( m_vecCtrl != null ) {
            // Steps enumeration for the actual Control owning the focus
            // while remembering last focusable Control.
            e = m_vecCtrl.elements();
            while( e.hasMoreElements() && bFlag != 1 ) {
                ctrl = (GuiCtrl)e.nextElement();
                if( ctrl.focusable() == true ) {
                    // When reaching the Control currently owning the
                    // focus, we must check if there is a previous found
                    // or we need to go on searching for the latest one.
                    if( ctrl.focus() == true ) {
                        if( ctrlPrev != null ) {
                            bFlag = 1;
                        }
                    }
                    // Remembers previous focusable Control only if not found yet
                    if( bFlag == 0 ) {
                        ctrlPrev = ctrl;
                    }
                }
            }
            
            if( setFocus(ctrlPrev) == true ) {
                return 1;
            }
        }
        return 0;
    }
    
    /******************************************************/
    
    
    private int lockedForMessageActiveAll(int value) {
        int i;
        int n;
        boolean state;
        
        state = (value != 0 ? true : false);
        
        // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        //
        // El siguiente código debe desaparecer
        //
        // - No tiene sentido activar o desactivar la colección
        // - Puede existir si, la funcionalidad es activar o descativar eventos.
        //
        setActive(state);
        // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        
        if( m_vecCtrl != null ) {
            for(  i = 0, n = m_vecCtrl.size(); i < n; i++) {
                ((GuiCtrl)m_vecCtrl.elementAt(i)).setActive(state);
            }
        }
        
        return 0;
    }
    
    private int lockedForMessageRedraw() {
        // Esta función es bastante simple gracias a lo papaya que es la plataforma J2ME
        // para el manejo de gráficos.
        
        // Establecer la variable repaint (protegida) de cada control, a true! ...
        
        redraw_all();
        
        repaint();
        
        // En un Arquitectura de simple - Thread, a continuación se opdataría por usar
        // una de las tecnicas para simcronizar la hebra de usuario y el AMS. Es decir
        // llamar a servicerepaint() o usar una variable mutex (pe. painting).
        // Este sería el único punto donde se cambiaría el código para tal caso. Dada
        // la ortogonalidad.
        return 0;
    }
    
    private int lockedForMessageShow() {
        
        if (m_vecCtrl.size() > 0) {
            // Redimensionamiento de los controles.
            resizeCtrls();
            // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
            //
            // El siguiente código debe desaparecer
            //
            // No tiene sentido bajo la premisa de que los controles pueden ser utilizados
            // si contenedor alguno.
            //
            for (int t = 0; t < m_vecCtrl.size(); t++){
                ((GuiCtrl)m_vecCtrl.elementAt(t)).init();
            }
            // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
            // Si no existe enfocado previo, establecer focus sobre el primero enfocable
            if( getFocus() == null ) {
                initFocus();
            }
            // Por defecto activar todos los controles.
            lockedForMessageActiveAll(1);
            // A continuación se debe solicitar redibujar el Gui.
            lockedForMessageRedraw();
        }
        return 0;
    }
    
    private int lockedForEvent(Msg msg) {
	boolean ret;
	
	ret = false;	
	if (!dispatchEventGuiGtrl(msg)) {
	    if (!dispatchEvent(msg)) {
		return 0;
	    }
	}
	
	m_Simlet.renderer(this);
	
	return 1;
    }
    
    private boolean dispatchEventGuiGtrl(Msg msg) {
	boolean ret;
	GuiCtrl ctrl;
	
	ret = false;
	
	ctrl = getFocus();
	if (ctrl != null) {
	    ret = ctrl.dispatchEvent(msg);
	    switch (msg.getEventCode()) {
	    case EventListener.COMMAND:
		m_Simlet.postEvent(msg);
		return true;
	    }
	}
	
	return ret;
    }
    
    private int lockedForRender() {
	int ret;
	
	ret = 0;

	// Aquí se debe llamar al render de cada control.
	// Para determnar el número de milsegundos a devolver.
	
        repaint();
	
	return ret;
    }
    
    protected void paint(final Graphics g) {
        lock(Gui.LOCKED_FOR_PAINT, 0, 0, 0, g);
    }
    
    
    public final int sendMessage(int ctrlID, int msgID, int param1, Object param2) {
        return lock( Gui.LOCKED_FOR_MESSAGE, ctrlID, msgID, param1, param2 );
    }
    
    public final boolean event(final Msg msg) {
        return (lock(Gui.LOCKED_FOR_EVENT, 0, 0, 0, msg) == 1);
    }
    
    public final int renderer() {
	return lock(LOCKED_FOR_RENDER, 0, 0, 0, null);
    }
    
    
    /**
     *  Creates a new GUI Control.
     *
     *  @param  type    ID Type of GUI Control
     *  @param  id      ID of the new GUI Control
     *  @param  Font    Font object
     *
     *  @return A GuiCtrl object
     */
    public final GuiCtrl createCtrl( int type, int id, final FontSingle font ) {
        return null;
    }
    
    
    /**
     * Modo directo de agregar un control al GUI.
     * Aqui el GUI supone de que es un control correcto y bien definido por el usuario.
     *
     * @param GuiCtrl gui   Control para el Gui.
     */
    public void addCtrl(final GuiCtrl ctrl) {
        sendMessage(GuiCtrl.INVALID_ITEM_ID, Gui.MSG_ADDCTRL, 0, ctrl);
        // Lo siguiente es lo correcto.
        //	return (sendMessage(GuiCtrl.INVALID_ITEM_ID, Gui.MSG_ADDCTRL, 0, ctrl) == 1);
    }
    
    /**
     * Elimina un control del contenedor, el control se ubica por su ID.
     *
     * @param id    identificador del control.
     */
    protected boolean delCtrl(int ctrlID){
        return (sendMessage(ctrlID, Gui.MSG_DELCTRL, 0, null) == 1);
    }
    
    
    /**
     * Elimina un control del contenedor
     *
     * @param GuiCtrl ctrl  Control a Eliminar en Contenedor.
     * @return boolean
     */
    public boolean delCtrl(GuiCtrl ctrl){
        return (sendMessage(GuiCtrl.INVALID_ITEM_ID, Gui.MSG_DELCTRL, 0, ctrl) == 1);
    }
    
    /**
     * Ubicamos el control buscado por el ID del mismo y lo devolvemos al solicitante.
     * @return GuiCtrl  Control
     */
    public GuiCtrl getCtrl(final int id){
        int c = m_vecCtrl.size();
        for (int t=0; t<c; t++){
            if ( ((GuiCtrl)m_vecCtrl.elementAt(t)).getID() == id ){
                return (GuiCtrl)m_vecCtrl.elementAt(t);
            }
        }
        return null;
    }
    
    public final void activeAll(boolean state) {
        sendMessage(GuiCtrl.INVALID_ITEM_ID, Gui.MSG_ACTIVEALL, (state ? 1 : 0), null);
    }
    
    
    /**
     * Calcula posiciones y pinta en pantalla los controles agregados al GUI.
     *
     * @return boolean  True si hay algun Control que mostrar o False si no hay.
     */
    public final void show() {
        sendMessage(GuiCtrl.INVALID_ITEM_ID, Gui.MSG_SHOW, 0, null);
    }
    
    
    public final void redraw() {
        sendMessage(GuiCtrl.INVALID_ITEM_ID, Gui.MSG_REDRAW, 0, null);
    }
    
    /**
     *  Returns the ID of the GUI Control owning the focus.
     *
     *  @return Control's ID owning the focus or INVALID_ITEM_ID if none
     */
    public final int getFocusID() {
        GuiCtrl ctrl = getFocus();
        if( ctrl != null ) {
            return ctrl.getID();
        }
        return GuiCtrl.INVALID_ITEM_ID;
    }
    
    public final GuiCtrl getFocus() {
        Enumeration e;
        GuiCtrl ctrl = null;
        e = m_vecCtrl.elements();
        while( e.hasMoreElements() ) {
            ctrl = (GuiCtrl)e.nextElement();
            if( ctrl.focus() == true ) {
                return ctrl;
            }
        }
        return null;
    }
    
    
    // Obtenemos maximos para alto y ancho de pantalla.
    private void resizeCtrls() {
    }
    
    
    /**
     *
     * Acciones de usuario
     *
     */
    protected void keyPressed(int keyCode) {
	m_Simlet.postEvent(EventListener.KEY_PRESSED, translateKey(keyCode), 0);
    }
    
    protected void keyReleased(int keyCode) {
	m_Simlet.postEvent(EventListener.KEY_RELEASED, translateKey(keyCode), 0);
    }
    
    /**
     *   importante
     */
    protected void showNotify() {
        redraw_all();
    }
    
    /**
     *  Translates a J2ME keycode to SIMLET keycode.
     *
     *  We use propietary keycodes to avoid compatibility issues on some handsets
     *  as also numeric keys 2,8,4,6 to be wrongly interpreted as Up, Down, Left and Right.
     *
     *  @param  jkey    a valid java key code
     *  @return int     a SIMLET key code
     */
    protected final int translateKey( final int jkey ) {
        int code = 0;
        switch( jkey ) {
	case -1:
	    code = Gui.KEY_UP;
	    break;
	case -2:
	    code = Gui.KEY_DOWN;
	    break;
	case -3:
	    code = Gui.KEY_LEFT;
	    break;
	case -4:
	    code = Gui.KEY_RIGHT;
	    break;
	case -5:
	    code = Gui.KEY_OK;
	    break;
	case -6:
	    code = Gui.KEY_SOFTLEFT;
	    break;
	case -7:
	    code = Gui.KEY_SOFTRIGHT;
	    break;
	case -8:
	    code = Gui.KEY_CLEAR;
	    break;
	case Canvas.KEY_POUND:
	    code = Gui.KEY_POUND;
	    break;
	case Canvas.KEY_STAR:
	    code = Gui.KEY_STAR;
	    break;
        }
        if( jkey >= Canvas.KEY_NUM0 && jkey <= Canvas.KEY_NUM9 ) {
            code = (jkey - Canvas.KEY_NUM0) + Gui.KEY_NUM0;
        }
        return code;
    }
    
    protected boolean dispatchEvent(final Msg msg) {
	return true;
    }
    
    /**
     *  Displays a PopUp window.
     *
     *  @param  str a String to show
     *  @param  fnt font to use
     *
     */
    public final void popUp( final String str, final FontSingle fnt ) {
        lock( Gui.LOCKED_FOR_MESSAGE, 0, Gui.MSG_POPUPSHOW, 0, str );
    }
    
    private final void popUp_paint( final Graphics g ) {
        // draw shadow box
        g.setColor(128,128,128);
        g.fillRect( (m_popupBx+m_popupBw+1), (m_popupBy+POPUP_SHADOW_W), POPUP_SHADOW_W, m_popupBh+1 );
        g.fillRect( (m_popupBx+POPUP_SHADOW_W), (m_popupBy+m_popupBh+1), m_popupBw+1, POPUP_SHADOW_W );
        // draw border box
        g.setColor(255,0,0); g.drawRect( m_popupBx, m_popupBy, m_popupBw, m_popupBh );
        // fill inner box
        g.setColor(232,232,232); g.fillRect( m_popupBx+1, m_popupBy+1, m_popupBw-1, m_popupBh-1 );
        // Draw text lines
        Enumeration e = m_vecPopUpLines.elements();
        int ty = m_popupStrY;
        while( e.hasMoreElements() ) {
            int[] aint = (int[])e.nextElement();
            m_fntPopUp.drawString( m_strPopUp, aint[0], aint[1], m_popupStrX, ty, g);
            ty += m_fntPopUp.getHeight();
        }
    }
    
    private void lockedForMessagePopUpShow( final String str ) {
        int popupStrWdt;
        int popupStrHgt;
        int numLines;
        
        m_fntPopUp = m_Simlet.getFontSys().medium;
        m_strPopUp = str;
        
        String[] astr = m_fntPopUp.splitStrLF( str );
        popupStrWdt = 0;
        for( int i=0; i< astr.length; i++ ) {
            int wdt = m_fntPopUp.getLenPixelString( astr[i] );
            if( wdt > popupStrWdt ) {
                popupStrWdt = wdt;
            }
        }
        
        popupStrWdt = popupStrWdt + (POPUP_BORDER<<1);
        if( popupStrWdt >= (getScreenWidth()-POPUP_SHADOW_W) ) {
            popupStrWdt = getScreenWidth() - POPUP_SHADOW_W;
        }
        numLines = m_fntPopUp.fitStringPixel(str, popupStrWdt, m_vecPopUpLines );
        
        popupStrWdt = 0;
        for( int i=0; i<numLines; i++ ) {
            int[] aint = (int[])m_vecPopUpLines.elementAt(i);
            int wdt = m_fntPopUp.getLenPixelString( m_strPopUp, aint[0], aint[1] );
            if( wdt > popupStrWdt ) {
                popupStrWdt = wdt;
            }
        }
        
        popupStrHgt = (numLines * m_fntPopUp.getHeight());
        m_popupStrX = (getScreenWidth() - popupStrWdt) >> 1;
        m_popupStrY = (getScreenHeight() - popupStrHgt) >> 1;
        m_popupBx = m_popupStrX - POPUP_BORDER;
        m_popupBy = m_popupStrY - (POPUP_BORDER>>1);
        m_popupBw = popupStrWdt + (POPUP_BORDER<<1);
        m_popupBh = popupStrHgt + POPUP_BORDER;
        if( m_popupBw >= getScreenWidth() ) { m_popupBx = 0; m_popupBw = getScreenWidth() - POPUP_SHADOW_W; }
        if( m_popupBh >= getScreenHeight() ) { m_popupBy = 0; m_popupBh = getScreenHeight() - POPUP_SHADOW_W; }
    }
    
    
    /**
     * Acceso sincronizado a controles.
     *
     */
    
    public final void setProperties(int ctrlID, int flags) {
        sendMessage(ctrlID, GuiCtrl.MSG_SETPROPERTIES, flags, null);
    }

    public final int getProperties( int ctrlID, int prop ) {
        return sendMessage(ctrlID, GuiCtrl.MSG_GETPROPERTIES, prop, null);
    }
    
    /**
     *  Sets a new text for a GUI Control.
     *  @param  id  GUI Control's ID you want to change its text
     *  @param  str the new text to put.
     */
    public final void setText(final int id, final String str ) {
        sendMessage( id, GuiCtrl.MSG_SETTEXT, 0, str );
    }
    
    /**
     * Esta es la función que debe sobrevivir
     *
     * @param ctrlId
     * @param str
     */
    public final void setText(int ctrlId, final StringBuffer strBuffer) {
        setText(ctrlId, strBuffer.toString());
    }
    
    public final int getText( final int id, final StringBuffer sb ) {
        return sendMessage( id, GuiCtrl.MSG_GETTEXT, 0, sb );
    }
    
    /**
     *  Sets a new text for an item of a GUI Control.
     *  @param  id      GUI Control's ID where is located the item
     *  @param  subid   ID if the item
     *  @param  str     the new text to put
     */
    public final void setItemText( final int id, final int subid, final String str ) {
        sendMessage( id, GuiCtrl.MSG_SETITEMTEXT, subid, str );
    }
    
    public final int getItemText( final int id, final int subid, final StringBuffer sb ) {
        return sendMessage( id, GuiCtrl.MSG_GETITEMTEXT, subid, sb );
    }
    
    /**
     *  Gets the current cursor position in a GUI Control.
     *  @param  id  GUI Control's ID
     *  @return the index cursor position
     */
    public final int getCursorPos( final int id ) {
        return sendMessage( id, GuiCtrl.MSG_GETCURSORPOS, 0, null );
    }
    
    /**
     *  Sets a new cursor position in a GUI Control.
     *
     *  GuiCtrl.LIST = sets cursor position at index position
     *
     *
     *  @param  id  GUI Control's ID you want to change its text
     *  @param  pos new index position of the cursor
     *  @return old cursor position
     */
    public final int setCursorPos( final int id, final int pos ) {
        return sendMessage( id, GuiCtrl.MSG_SETCURSORPOS, pos, null );
    }
    
    /**
     *  Selects a GUI Control's Item by its ID.
     *
     *  GuiCtrl.LIST = sets cursor position at Item ID
     *
     *  @param id       ID of the GUI Control
     *  @param subid    ID of the item in a GUI Control
     */
    public final void selectItem( final int ctrlId, int itemId ) {
        sendMessage(ctrlId, GuiCtrl.MSG_SELECTITEM, itemId, null );
    }
    
    /**
     *  Gets a count value for this GUI Control.
     *  Note: some Controls may not return a value.
     *
     *  @param id       ID of the GUI Control
     */
    public final int getCount( final int id ) {
        return sendMessage( id, GuiCtrl.MSG_GETCOUNT, 0, null );
    }
    
    /**
     * Devuelve el identificador de un elemento de un control contenedor;
     * por ejemplo, un control GuiCtrlList, GuiCtrlSoftMenu.
     *
     * @param ctrlId Identificador del control contenedor.
     *
     * @return Si tiene éxito devuelve el identificador de un elemento en
     *         caso contrario devuelve GuiCtrlItem.INVALID_ITEM_ID.
     */
    public final int getItemId(final int ctrlId) {
        return sendMessage(ctrlId, GuiCtrl.MSG_GETITEMID, 0, null );
    }
    
    /**
     *  Gets a value for this GUI Control.
     *
     *  @param id       ID of the GUI Control
     */
    public final int getValue( final int id ) {
        return sendMessage( id, GuiCtrl.MSG_GETVALUE, 0, null );
    }
    
    public final void setValue( final int id, final int val  ) {
        sendMessage( id, GuiCtrl.MSG_SETVALUE, val, null );
    }
    
    /**
     * Devuelve el identificador del primer Item seleccionado.
     *
     * Este mensaje aplica a los controles del tipo GuiCtrl::MULTIOPT.
     *
     * @param ctrlID Identificador del control.
     *
     * @return Si no hay un item seleccionado devuelve GuiCtrl::INVALID_ITEM_ID.
     */
    public final int getInitId(final int ctrlID) {
        return sendMessage( ctrlID, GuiCtrl.MSG_GETINITID, 0, null );
    }
    
    /**
     * Devuelve el identificador del siguiente Item seleccionado.
     *
     * Este mensaje aplica a los controles del tipo GuiCtrl::MULTIOPT.
     *
     * @param ctrlID Identificador del control.
     *
     * @return Si no hay siguiente item seleccionado devuelve GuiCtrl::INVALID_ITEM_ID.
     */
    public final int getNextId(final int ctrlID) {
        return sendMessage( ctrlID, GuiCtrl.MSG_GETNEXTID, 0, null );
    }
    
    /**
     * Recupera el texto del primer item seleccionado.
     *
     * Este mensaje aplica a los controles del tipo GuiCtrl::MULTIOPT.
     *
     * @param ctrlID Identificador del control.
     * @param out Una instancia de la clase StringBuffer.
     *
     * @return Devuelve la longitud del texto del primer item seleccionado. Si
     *         no hay un item seleccionado devuelve 0.
     */
    public final int getInitText(final int ctrlID, StringBuffer out) {
        return sendMessage( ctrlID, GuiCtrl.MSG_GETINITTEXT, 0, out );
    }
    
    /**
     * Recupera el texto del siguiente item seleccionado.
     *
     * Este mensaje aplica a los controles del tipo GuiCtrl::MULTIOPT.
     *
     * @param ctrlID Identificador del control.
     * @param out Una instancia de la clase StringBuffer.
     *
     * @return Devuelve la longitud del texto del siguiente item seleccionado. Si
     *         no hay un siguiente item seleccionado devuelve 0.
     */
    public final int getNextText(final int ctrlID, StringBuffer out) {
        return sendMessage( ctrlID, GuiCtrl.MSG_GETNEXTTEXT, 0, out );
    }
    
    /**
     * Añade texto a un control.
     *
     * @param ctrlId Identificador del control.
     * @param param Para un control del tipo GuiCtrlList este parámetro representa el
     *              identificador del item. Para un control que texto; por ejemplo,
     *              GuiCtrlText, CuiCtrlPrompt, este parámetro representa el offset
     *              del texto.
     */
    public final int addText( final int ctrlId, final int param, final String text) {
        return sendMessage(ctrlId, GuiCtrl.MSG_ADDTEXT, param, text);
    }
    
    /**
     * Esta función es la que debe sobrevivir.
     *
     * @param ctrlId
     * @param param
     * @param text
     *
     * @return
     */
    public final int addText(int ctrlId, int param, final StringBuffer strBuffer) {
        return addText(ctrlId, param, strBuffer.toString());
    }
    
    /**
     *  Gets the value of an item.
     *
     *  CtrlMultiOpt = returns the Check state
     */
    public final int getItemValue(int ctrlID, int idItem){
        return sendMessage(ctrlID, GuiCtrl.MSG_GETITEMVALUE, idItem, null);
    }
    
    public final void setItemValue(int ctrlID, int idItem, Integer state){
        sendMessage(ctrlID, GuiCtrl.MSG_SETITEMVALUE, idItem, state );
    }
    
    /**
     *  Adds an item to a GUI Control.
     *
     *  @param  ctrlId  ID of the Control which will contain the item
     *  @param  item    Object item
     */
    public final void addItem( final int ctrlID, final Object item ) {
        sendMessage( ctrlID, GuiCtrl.MSG_ADDITEM, 0, item );
    }
    
    /**
     *  Sets the focus on a GUI Control.
     *
     *  @param  ctrlID  ID of the Control to set focus on
     */
    public final void setFocus( final int ctrlID ) {
        sendMessage( ctrlID, Gui.MSG_SETFOCUS, 0, null );
    }
    
    
    // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    
    // El siguiente código debe desaparecer.
    //
    // - No son requeridos para resolver el problema del GUI.
    // - Algunos tienen nombres no apropiados.
    //
    
    // Flag to ensure that MODULE and DISPATCH EVENT receives only ONE keystate
    // message (Pressed or Released) while allowing Controls to be updated constantly.
    private     boolean     m_bModMsg;
    
    public static final byte BORDER_FONT = 2;  // # pixels border spacing between Control Box and text
    public static final byte BORDER_CTRL = 0;  // Espaciado izquierdo y derecho del control con la pantalla.
    
    // ---- KEYBOARD CODES MAPPING
    //public static final short   KEYDELAY_MS = 150;
    //private int                 m_tkKeyDelay;       // key delay (ticks)
    
    //protected int     m_bKeyState;
    //private   int     m_currentKey;
    
    private boolean     m_bGuiActive;     // [RESERVED] T/F GUI updates or freeze
    

    private void lockedForMessagePopUpHide() {
        m_strPopUp = null;
    }
    
    public final void popUp_Close() {
        lock( Gui.LOCKED_FOR_MESSAGE, 0, Gui.MSG_POPUPHIDE, 0, null );
    }
    
    public final boolean popUp() {
        return m_strPopUp != null ? true : false;
    }
    
    public final void setActive( final boolean state ) {
        m_bGuiActive = state;
    }
    
    /**
     * Width of display terminal
     *
     * @return int - width of display in pixels
     */
    public static final int getScreenWidth() {
        // Si las variables son estáticas no requieren función de acceso!!!
        return m_SCREENWIDTH;
    }
    
    /**
     * Height of display terminal
     *
     * @return int - height of display in pixels
     */
    public static final int getScreenHeight() {
        // Si las variables son estáticas no requieren función de acceso!!!
        return m_SCREENHEIGHT;
    }
    
//     protected void setKeyDelay( final int delay ) {
//         m_tkKeyDelay = delay;
//     }
    
//     public int getKeyDelay() {
//         return m_tkKeyDelay;
//     }
    
//     protected void setKeyState( final boolean state ) {
//         if( state == true ) {
//             m_bKeyState = EventListener.KEY_PRESSED;
//         } else {
//             m_bKeyState = EventListener.KEY_RELEASED;
//         }
//     }
        
}
